/**
 * WebSocket服务器启动程序
 * 依赖：
 */	

//使用严格模式
'use strict';

//守护node的进程，避免因为发生错误而导致程序崩溃
/**process.on('uncaughtException', function(err) {
	//打印错误内容
	console.log(err);
	//打印调用栈以便调试
	console.log(err.stack);
});**/

//服务器配置
var config = require('./config').config;

//状态变量
var status = {
	//redis服务器是否已连接状态
	redisConnected: false
}

//引入Q模块实现promise语法
var Q = require('q');

//引入fileSystem模块操作文件
var fs = require('fs');

//引入url模块解析URL
var url = require('url');

//引入queryString模块转换数据
var qs = require('querystring');

//引入path模块解析路径
var path = require('path');

//引入multiparty模块来解析form-data数据
var mp = require('multiparty');

//引入http模块构造http服务器
var http = (config.ssl) ? require('https') : require('http');

//引入ws模块建立WebServer
var ws = require('ws').Server;

//引入mongodb操作模块
var mongo = require('./mongo');

//引入redis操作模块
var redis = require('./redis');

//引入工具模块
var util = require('./util');

//声明全局http服务器实例变量
var httpServer = null;

//声明全局websocket服务器实例变量
var wsServer = null;

//声明全局mongodb连接池，以storeId做为key
var dbs = {};

//连接redis数据库
redis.connect(config.redis.host, config.redis.port, config.redis.options).then(function(client) {
	client.on('connect', function() {
		console.log('Redis已连接');
		//已连接将redisConnected状态变为true
		status.redisConnected = true;
	});
	client.on('error', function(err) {
		console.log('Redis连接失败', err);
		//连接失败将redisConnected状态变为false
		status.redisConnected = false;
	});
});

//建立一个http服务器，根据配置决定是否应用SSL
util.createHttpServer(config, function(req, res) {
	console.log('远程访问域名为', req.headers.origin);
	if(config.allowOrigin.indexOf(req.headers.origin) != -1) {
		res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
	}
	res.setHeader('Access-Control-Allow-Methods', 'POST,GET');
	res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
	//请求处理器处理请求
	requestHandler(req, res).then(function(data) {
		console.log('返回的数据', data);
		//如果是一个带有echo和broadcast的数组则把第二个元素输出且广播第四个元素内容
		if(data[0] && data[0] == 'echo' && data[2] && data[2] == 'broadcast') {
			let echoData = data[1];
			let broadcastData = data[3];
			//广播消息到服务端
			wsBroadcast(broadcastData.storeId, broadcastData.type, broadcastData.event, broadcastData.data).then(function() {
				//写入head
				res.writeHead(200, {
					'content-type': 'text/plain'
				});
				res.write(echoData);
				res.end();
			}, function(errCode) {
				//返回错误
				util.getError(errCode).then(function(err) {
					console.log(err);
					res.writeHead(200, {
						'content-type': 'application/json'
					});
					res.write(err);
					res.end();
				});
			});
		}
		//如果是一个带有echo的数组则把第二个元素直接输出
		else if(data[0] && data[0] == 'echo') {
			//写入head
			res.writeHead(200, {
				'content-type': 'text/plain'
			});
			res.end(data[1]);
		}
		//如果是一个带有broadcast广播的数组则把广播内容广播给所有对应storeId的服务端
		else if(data[0] && data[0] == 'broadcast') {
			var broadcastData = data[1];
			//广播消息到服务端
			wsBroadcast(broadcastData.storeId, broadcastData.type, broadcastData.event, broadcastData.data).then(function() {
				res.writeHead(200, {
					'content-type': 'text/plain'
				});
				//广播完毕后返回1
				res.end(JSON.stringify({
					errCode: '0',
					data: 1
				}));
			}, function(errCode) {
				//返回错误
				util.getError(errCode).then(function(err) {
					console.log(err);
					res.writeHead(200, {
						'content-type': 'application/json'
					});
					res.write(err);
					res.end();
				});
			});
		}
		else if(data[0] && data[0] == 'file') {
			let contentType = data[1]['contentType'];
			let fileContent = data[1]['fileContent'];
			let encoding = data[1]['encoding'] ? ';charset=' + data[1]['encoding'] : '';
			let contentLength = data[1]['contentLength'];
			let base64 = data[1]['base64'];
			let head = {};
			head['content-type'] = contentType + encoding;
			if(contentLength) {
				head['content-length'] = contentLength;
			}
			//写入head
			res.writeHead(200, head);
			if(base64) {
				fileContent = new Buffer(fileContent, 'base64').toString('binary');
				res.write(fileContent, 'binary');
				res.end();
			}
			else {
				res.end(fileContent);
			}
		}
		else {
			//写入head
			res.writeHead(200, {
				'content-type': 'application/json'
			});
			//处理请求成功并返回json字符串
			res.end(JSON.stringify({
				errCode: '0',
				data: data
			}));
		}
	},function(errCode) {
		//处理请求失败返回错误
		util.getError(errCode).then(function(err) {
			console.log(err);
			res.writeHead(200, {
				'content-type': 'application/json'
			});
			res.write(err);
			res.end();
		});
	});
}).then(function(server) {
	//在http服务器基础上建立websocket服务器
	wsServer = new ws({server: server});
	//客户端连接事件监听
	wsServer.on('connection', wsConnected);
	console.log('HTTP与WebSocket服务器已创建');
});

//WebSocket连接函数
var wsConnected = function(client) {
	//提前声明未来将会存放的值，如果为null则此连接还未被授权使用
	client.storeId = null;
	client.name = null;
	client.timestamp = parseInt(Date.now() / 1000);
	//客户端断开事件监听
	client.on('close', function() {
		wsClose(this);
	});
	//收到客户端消息事件监听
	client.on('message', function(msg) {
		//消息处理器函数
		wsMsgHandler(this, msg).then(function(data) {
			if(data != 1) {
				//消息处理成功回送一条json消息
				client.send(JSON.stringify({
					errCode: '0',
					data: data
				}));
			}
		}, function(errCode) {
			if(errCode.interfacePair) {
				console.log('接口配对值：' + errCode.interfacePair);
				client.send(JSON.stringify({
					errCode: errCode.errCode,
					data: {
						type: 'interface',
						pair: errCode.interfacePair
					}
				}));
			}
			else {
				//回送一条错误json消息
				util.getError(errCode).then(function(err) {
					console.log(err);
					client.send(err);
					client.close();
				});
			}
		});
	});
}

//WebSocket关闭函数
var wsClose = function(client) {
	//判断是否存在storeId和name和时间戳
	util.isSet(client.storeId, client.name, client.timestamp).then(function() {
		//广播某客服下线通知
		wsBroadcast(client.storeId, 'sys', 'wsClose', {
			serviceName: client.name
		}).then(function() {
			//广播成功清除三值
			client.storeId = null;
			client.name = null;
			client.timestamp = null;
		});
	});
}

//WebSocket消息处理函数
var wsMsgHandler = function(client, msg) {
	var deferred = Q.defer();
	//检查当前客户端连接是否已完成配对操作
	wsCheckPair(client, msg).then(function(data) {
		//判断是否是完成配对
		if(!data._interface) {
			//完成配对，不处理消息等待正式消息
			deferred.resolve({
				type: 'sys',
				event: 'wsPair',
				sender: client.serviceId,
				msg: data
			});
		}
		else {
			//连接已与服务端配对
			//判断是否存在接口这个参数
			if(data._interface) {
				var storeId = data.storeId;
				//拼合接口路径
				var interfacePath = config.basePath + '/interfaces/' + data._interface + '.js';
				console.log(interfacePath);
				//判断接口文件是否存在
				util.fsExists(interfacePath).then(function() {
					//连接商户的数据库
					return storeId ? connectDB(storeId) : {};
				}, function() {
					//WebSocket中调用的接口文件不存在
					deferred.reject({
						interfacePair: data.pair,
						errCode: '-0043'
					});
				}).then(function(db) {
					if(data.data) {
						getAccessToken().then(function(accessToken) {
							//获得accessToken
							data.data.accessToken = accessToken;
							//引入接口
							var _interface = require(interfacePath);
							//传入服务员信息
							data.data.serviceInfo = {
								//服务员openId
								serviceId: client.serviceId,
								//服务员微信名
								serviceName: client.name
							}
							console.log(data);
							
							//调用接口入口函数
							_interface.index({}, data.data, config, redis, db).then(function(result) {
								if(result[0] == 'broadcast') {
									let broadcastData = result[1];
									wsBroadcast(broadcastData.storeId, broadcastData.type, broadcastData.event, broadcastData.data).then(function() {
										//处理成功返回结果
										deferred.resolve({
											type: 'interface',
											event: data.pair,
											sender: client.serviceId,
											msg: 1
										});
									}, function(errCode) {
										//处理失败返回错误码
										deferred.reject({
											interfacePair: data.pair,
											errCode: errCode
										});
									});
								}
								else {
									//处理成功返回结果
									deferred.resolve({
										type: 'interface',
										event: data.pair,
										sender: client.serviceId,
										msg: result
									});
								}
							}, function(errCode) {
								//处理失败返回错误码
								deferred.reject({
									interfacePair: data.pair,
									errCode: errCode
								});
							});	
							
						}, function(errCode) {
							//获取accessToken失败，返回错误码
							deferred.reject({
									interfacePair: data.pair,
									errCode: errCode
							});
						});
					}
					else {
						//调用接口的参数中不存在data
						deferred.reject({
							interfacePair: data.pair,
							errCode: '-0061'
						});
					}
				}, function() {
					//连接商户数据库失败
					deferred.reject({
						interfacePair: data.pair,
						errCode: '-0012'
					});
				});
			}
			else {
				//WebSocket收到的消息中未找到接口参数
				deferred.reject({
					property: 'interface',
					errCode: '-0042'
				});
			}
		}
	}, function(errCode) {
		//检查是否有未配对的连接超过有效期
		checkWsExpire();
		//未配对，返回错误码
		deferred.reject(errCode);
	});
	return deferred.promise;
}

//WebSocket检查配对函数
var wsCheckPair = function(client, msg) {
	var deferred = Q.defer();
	//判断消息是否为json字符串
	util.isJson(msg).then(function(data) {
		//在客户端连接中是否存在storeId和时间戳
		util.isSet(client.storeId, client.serviceId, client.name, client.timestamp).then(function() {
			//连接实例中存在三值即为已配对状态
			deferred.resolve(data);
		}, function() {
			console.log('登录',data);
			//不存在则检查此次消息是否为请求配对，要求storeId以及3rdSession用于验证身份
			util.isSet(data.storeId, data._3rdSession).then(function() {
				//检查3rdSession合法性并返回openId和sessionKey
				check3rdSession(data._3rdSession).then(function(_3rdSessionData) {
					//连接到店铺数据库
					connectDB(data.storeId).then(function(storeDB) {
						//从店铺数据库读取授权客服列表
						storeDB.select('allowServices', {id: _3rdSessionData.openId}).then(function(allowServiceList) {
							console.log(allowServiceList);
							//此客服存在于授权列表中
							if(allowServiceList.length > 0) {
								var allowService = allowServiceList[0];
								//将配对信息写入连接实例
								client.storeId = data.storeId;
								client.serviceId = allowService.id;
								client.name = allowService.name;
								client.auth = allowService.auth;
								client.timestamp = parseInt(Date.now() / 1000);
								client.tableNums = allowService.tableNums;
								// wsServer.clients.forEach(function(_client) {
								// 	if(client.serviceId == _client.serviceId && client.timestamp != _client.timestamp) {
								// 		console.log(client.serviceId, _client.serviceId, client.timestamp, _client.timestamp);
								// 		wsClose(this);
								// 		_client.close();
								// 	}
								// });
								//广播客服上线通知
								wsBroadcast(client.storeId, 'sys', 'wsConnect', {
									serviceName: allowService.name
								}).then(function() {
									//广播成功
									deferred.resolve({
										serviceName: allowService.name,
										serviceAuth: allowService.auth,
										serviceTableNums: allowService.tableNums
									});
								}, function(errCode) {
									//广播失败，返回错误码
									deferred.reject(errCode);
								});
							}
							else {
								if(data.storeId == 'canting') {
									let serviceName = '审核测试号';
									let serviceAuth = 'admin';
									//将配对信息写入连接实例
									client.storeId = data.storeId;
									client.serviceId = _3rdSessionData.openId;
									client.name = serviceName;
									client.auth = serviceAuth;
									client.timestamp = parseInt(Date.now() / 1000);
									client.tableNums = '*';
									// wsServer.clients.forEach(function(_client) {
									// 	if(client.serviceId == _client.serviceId && client.timestamp == _client.timestamp) {
									// 		console.log(client.serviceId, _client.serviceId, client.timestamp, _client.timestamp);
									// 		_client.close();
									// 	}
									// });
									//广播客服上线通知
									wsBroadcast(client.storeId, 'sys', 'wsConnect', {
										serviceName: serviceName
									}).then(function() {
										//广播成功
										deferred.resolve({
											serviceName: serviceName,
											serviceAuth: serviceAuth,
											serviceTableNums: '*'
										});
									}, function(errCode) {
										//广播失败，返回错误码
										deferred.reject(errCode);
									});
								}
								else {
									//未授权此客服
									deferred.reject('-0031');
								}
							}
						}, function() {
							//从店铺数据库读取授权客服列表失败
							deferred.reject('-0030');
						});
					}, function() {
						//客服消息处理中无法连接到对应商户的数据库
						deferred.reject('-0029');
					});
				}, function() {
					//客服未登录不允许配对WebSocket连接
					deferred.reject('-0028');
				});
			}, function() {
				//不存在双值的请求配对，非配对请求消息，不作处理返回失败
				deferred.reject('-0026');
			});
		});
	}, function() {
		//消息内容不是一个json字符串
		deferred.reject('-0025');
	});
	return deferred.promise;
}

//广播消息函数
var wsBroadcast = function(storeId, type, event, msg) {
	var deferred = Q.defer();
	//尝试广播消息
	try {
		//判断当前客服连接个数是否为0，如果为0则直接返回成功
		if(wsServer.clients.size == 0) {
			deferred.resolve();
		}
		//当前客户端index
		var clientIndex = 0;
		console.log(wsServer.clients);
		console.log(wsServer.clients.size);
		console.log('消息发给' + storeId);
		//遍历所有连接实例
		wsServer.clients.forEach(function(client) {
			//判断遍历出的客户端的storeId和发送消息的客户端的storeId是否一样
			if(client.storeId == storeId) {
				let sendFun = function() {
					console.log('广播消息', {
						type: type,
						event: event,
						sender: client.serviceId,
						msg: msg
					});
					//对遍历出的客户端发送消息
					client.send(JSON.stringify({
						//设置错误码为0
						errCode: '0',
						//设置数据
						data: {
							//设置发送数据的主体类型，如sys
							type: type,
							//设置消息所属事件
							event: event,
							//设置消息发送者ID
							sender: client.serviceId,
							//设置发送的数据
							msg: msg
						}
					}));
				}
				if(event == 'storeFun' && client.tableNums != '*') {
					console.log('服务消息处理');
					if(client.tableNums.length == 0) {
						deferred.resolve();
						return deferred.promise;
					}
					else {
						let found = false;
						for(let tableNumIndex in client.tableNums) {
							if(client.tableNums[tableNumIndex] == msg.tableNum) {
								found = true;
							}
							if(tableNumIndex == client.tableNums.length - 1) {
								if(found) {
									sendFun();
								}
								else {
									deferred.resolve();
									return deferred.promise;
								}
							}
						}
					}
				}
				else {
					console.log('正常消息处理');
					sendFun();
				}
			}
			//判断是否已经遍历完毕
			if(++clientIndex == wsServer.clients.size) {
				console.log('广播完成');
				//返回广播完成
				deferred.resolve();
			}
		});
	}
	catch(e) {
		console.log(e);
		//广播内容时发生错误
		deferred.reject('-0027');
	}
	return deferred.promise;
}

//检查WebSocket未配对的连接的有效期，超过指定秒数未配对则失效关闭
var checkWsExpire = function() {
	//遍历所有客户端
	wsServer.clients.forEach(function(client) {
		//检查是否已超时限
		if(parseInt(Date.now() / 1000) - client.timestamp > config.noPairWsExpire) {
			//关闭客户端连接
			client.close();
		}
	});
}

//HTTP请求处理函数
var requestHandler = function(req, res) {
	var deferred = Q.defer();
	//如果redis未连接则返回错误-0005
	if(!status.redisConnected) {
		deferred.reject('-0005');
	}
	//将URL解析为URL对象
	var urlObj = url.parse(req.url, true);
	//获得URL中域名后的路径
	var uri = urlObj.pathname;
	//获得URL中GET传值数据
	var getData = urlObj.query;
	//获得请求的数据的MIME类型
	var contentType = req.headers['content-type'];
	//拼接接口路径
	var interfacePath = config.interfacesPath + uri + '.js';
	//获得接口名称
	var interfaceName = path.parse(interfacePath).name;
	//存放客户端ip的变量
	var clientIP = 'unknown';
	//尝试获取请求端的真实IP
	if(req.headers['x-forwarded-for']) {
		//存在代理服务器获取到真实ip
		clientIP = req.headers['x-forwarded-for'].split(',')[0];
	}
	//不存在代理服务器
	if(clientIP == 'unknown') {
		//直接获取ip地址信息并分割出ip
		var temp = req.connection.remoteAddress.split(':');
		if(temp.length > 0) {
			clientIP = temp[temp.length - 1];
		}
	}
	console.log('IP：' + clientIP);
	//判断接口文件是否存在
	util.fsExists(interfacePath).then(function() {
		//引入接口文件
		var _interface = require(interfacePath);
		console.log(contentType);
		getAccessToken().then(function(accessToken) {
			console.log('AccessToken为:' + accessToken);
			//判断收到的是否是POST数据
			util.isPost(req).then(function() {
				dataParse(contentType, req).then(function(_postData) {
					//嵌入clientIP值
					_postData.clientIP = clientIP;
					//嵌入accessToken值
					_postData.accessToken = accessToken;
					//调用接口处理器将拼接完毕的post数据转换成对象并作为参数传入
					interfaceDataHandler(interfaceName, _interface, getData, _postData).then(function(data) {
						//接口处理成功并返回数据
						deferred.resolve(data);
					}, function(errCode) {
						//接口处理失败并返回错误码
						deferred.reject(errCode);
					});
				}, function(errCode) {
					errCode = errCode || '-0015';
					//请求字符串无法转换为对象
					deferred.reject(errCode);
				});
			}, function() {
				//嵌入clientIP值
				getData.clientIP = clientIP;
				//嵌入accessToken值
				getData.accessToken = accessToken;
				//不是POST数据作为GET数据处理，调用接口处理器
				interfaceDataHandler(interfaceName, _interface, getData, {}).then(function(data) {
					//接口处理成功并返回数据
					deferred.resolve(data);
				}, function(errCode) {
					//接口处理失败并返回错误码
					deferred.reject(errCode);
				});
			});
		}, function(errCode) {
			//获取accessToken失败
			deferred.reject(errCode);
		});
	}, function() {
		//接口文件不存在
		deferred.reject('-0001');
		console.log('找不到接口文件：' + interfacePath);
	});
	return deferred.promise;
}

//数据转换函数，根据收到的不同content-type来转换数据

var dataParse = function(contentType, req) {
	var deferred = Q.defer();
	//转换函数列表
	console.log('进入数据转换');
	var parseFuns = {
		xml: function() {
			console.log('数据类型为xml');
			parseFuns.listener().then(function(data) {
				console.log('xml原文：', data);
				try {
					//使用JSON转换字符串为对象
					data = JSON.parse(data);
					deferred.resolve(data);
					return deferred.promise;
				}
				catch(e) {
					return util.xml2obj(data);
				}
			}, function() {
				//监听数据失败
				deferred.reject();
			}).then(function(objData) {
				console.log('xml数据：', objData);
				//使用querystring转换字符串为对象
				deferred.resolve(objData);
			}, function() {
				//转换XML为对象数据失败
				deferred.reject();
			});
		},
		json: function() {
			console.log('数据类型为json');
			parseFuns.listener().then(function(data) {
				console.log(data);
				//使用JSON转换字符串为对象
				deferred.resolve(JSON.parse(data));
			}, function() {
				//监听数据失败
				deferred.reject();
			});
		},
		formData: function() {
			console.log('数据类型为formData');
			//判断tamp文件夹是否存在

			if(!fs.existsSync(config.uploadPath + 'temp/')) {
				//temp文件夹不存在，新建该文件夹

				fs.mkdirSync(config.uploadPath + 'temp/');
			}
			//实例化multiparty的Form

			var form = new mp.Form();
			//设置编码

			form.encoding = 'utf-8';
			//设置上传文件的最大大小

			form.maxFilesSize = config.uploadMaxSize * 1024 * 1024;
			//设置放置文件的文件夹

			form.uploadDir = config.uploadPath + 'temp/';
			//使用multiparty的Form转换字符串为对象

			form.parse(req, function(err, fields, files) {
				if(err) {
					//转换失败

					console.log(err);
					deferred.reject('-0019');
				}
				else {
					//将字段全部从数组转为单值

					for(var key in fields) {
						if(fields[key][0]) {
							fields[key] = fields[key][0];
						}
					}
					//判断是否存在storeId

					util.isSet(fields.storeId).then(function() {
						//转换成功

						deferred.resolve({
							//formData字段数据

							fields: fields,
							//上传的文件数组

							files: files,
							//店铺ID

							storeId: fields.storeId
						});
					}, function() {
						//storeId不存在

						deferred.reject('-0013');
					});
				}
			});
		},

		wwwwForm: function() {
			parseFuns.listener().then(function(data) {
				data = qs.parse(data);
				console.log(data);
				//使用JSON转换字符串为对象
				deferred.resolve(data);
			}, function() {
				//监听数据失败
				deferred.reject();
			});
		},

		//监听post数据函数

		listener: function() {
			var deferred2 = Q.defer();
			//定义空字符串变量用于存储拼接的post数据

			var buffer = '';
			//设置监听接收post数据函数

			req.addListener('data', function(chunk) {
				//拼接POST数据字符串

				buffer += chunk;
			});
			//设置监听POST数据接收完毕函数

			req.addListener('end', function() {
				deferred2.resolve(buffer);
			});
			return deferred2.promise;
		}
	}
	//content-type列表，指向对应处理函数

	var contentTypes = {
		'application/x-www-form-urlencoded': parseFuns.wwwwForm,
		'application/json': parseFuns.json,
		'application/xml': parseFuns.xml,
		'text/xml': parseFuns.xml,
		'multipart/form-data': parseFuns.formData
	}
	//数据类型列表类型个数

	var typeSum = Object.keys(contentTypes).length;
	//当前数据类型index

	var typeIndex = 0;
	//查找当前收到的数据的content-type是否在content-type列表中

	for(var type in contentTypes) {
		//如果能够在content-type中找到对应类型

		if(contentType.indexOf(type) != -1) {
			console.log('检索到的类型' + type);
			//调用该类型的数据转换处理函数

			contentTypes[type]();
			break;
		}
		//判断数据类型是否不属于类型列表中的其中一种

		if(++typeIndex == typeSum) {
			//没有找到对应数据类型

			deferred.reject();
		}
	}
	return deferred.promise;	
}

//接口处理器
var interfaceDataHandler = function(interfaceName, _interface, getData, postData) {
	var deferred = Q.defer();
	//检查是否有权限调用接口
	checkPermission(interfaceName, getData, postData).then(function(data) {
		//得到openId
		postData.openId = data.openId;
		//得到sessionKey
		postData.sessionKey = data.sessionKey;
		//获取storeId
		if(postData.SessionFrom) {
			var storeData = postData.SessionFrom.split('&');
			if(storeData[0] && storeData[1]) {
				postData.storeId = storeData[0];
				postData.tableNum = storeData[1];
			}
			else {
				deferred.reject('-0037');
			}
		}
		else {
			postData.storeId = postData.storeId || getData.storeId || '';
		}
		//检查storeId合法性
		checkStoreId(interfaceName, postData.storeId).then(function(id) {
			//返回数据库连接实例
			return id ? connectDB(id) : {};
		}, function() {
			//参数中未找到storeId，返回-0013
			deferred.reject('-0013');
		}).then(function(db) {
			console.log('接收GET数据：', getData);
			console.log('接收POST数据：', postData);
			//提供再连接数据库的方法
			db.connectDB = connectDB;
			try {
			//有权限调用接口，调用接口入口函数
			_interface.index(getData, postData, config, redis, db, wsServer).then(function(data) {
				//接口处理成功并返回数据
				deferred.resolve(data);
			}, function(errCode) {
				//接口处理失败并返回错误码
				deferred.reject(errCode);
			});}
			catch(e) {
				console.log(e);
			}
		}, function() {
			//连接mongodb数据库失败，返回-0012
			deferred.reject('-0012');
		});
	}, function(errCode) {
		//无权限调用接口并返回错误码
		deferred.reject(errCode);
	});
	return deferred.promise;
}

//检查调用接口权限函数
var checkPermission = function(interfaceName, getData, postData) {
	var deferred = Q.defer();
	var storeId = getData.storeId || postData.storeId || '';
	if(interfaceName == 'getUserData' && storeId == '') {
		deferred.resolve({
			openId: '',
			sessionKey: ''
		});
	}
	else {
		//检查接口是否在白名单中
		checkInterfaceAllow(interfaceName).then(function() {
			//在白名单中，无需检查3rdSession直接允许调用
			deferred.resolve({
				openId: '',
				sessionKey: ''
			});
		}, function() {
			//未在白名单中需要检查3rdSession是否合法
			//检查post参数中是否有3rdSession值
			util.isSet(postData['_3rdSession']).then(function() {
				//存在3rdSession检查其合法性
				check3rdSession(postData['_3rdSession']).then(function(data) {
					//3rdSession合法返回允许调用接口并返回openId和sessionKey
					deferred.resolve(data);
				}, function(errCode) {
					//3rdSession非法返回不允许调用接口
					deferred.reject(errCode);
				});
			}, function() {
				//post数据中不存在3rdSession返回不允许调用接口
				deferred.reject('-0002');
			});
		});
	}
	return deferred.promise;
}

//检查接口是否在白名单函数
var checkInterfaceAllow = function(interfaceName) {
	var deferred = Q.defer();
	//遍历白名单配置
	for(var nameIndex in config.interfaceAllow) {
		if(config.interfaceAllow[nameIndex] == interfaceName) {
			//白名单中存在该接口
			deferred.resolve();
		}
	}
	//白名单中不存在该接口
	deferred.reject();
	return deferred.promise;
}

//检查3rdSession合法性函数
var check3rdSession = function(_3rdSession) {
	var deferred = Q.defer();
	//从redis中读取3rdSession为key的所存储值
	redis.get(_3rdSession).then(function(data) {
		//检查是否json字符串
		util.isJson(data).then(function(jsonData) {
			//3rdSession指向的数据中是否存在sessionKey和openId
			if(util.isSet(jsonData.sessionKey) && util.isSet(jsonData.openId)) {
				//3rdSession合法
				deferred.resolve(jsonData);
			}
			else {
				//3rdSession指向的值非法返回-0004
				deferred.reject('-0004');
			}
		}, function() {
			//3rdSession指向数据不存在或有误
			deferred.reject('-0004');
		});
	},function(errCode) {
		//读取3rdSession缓存失败返回-0003
		deferred.reject('-0003');
	});
	return deferred.promise;
}

//检查storeId函数
var checkStoreId = function(interfaceName, storeId) {
	var deferred = Q.defer();
	//判断是否存在storeId
	if(storeId && storeId != '') {
		connectDB('common').then(function(commonDB) {
			return commonDB.select('storeList', {
				storeId: storeId
			});
		}, function() {
			deferred.reject();
		}).then(function(result) {
			if(result && result.length > 0) {
				//存在直接返回storeId
				deferred.resolve(storeId);
			}
			else {
				deferred.reject();
			}
		}, function() {
			deferred.reject();
		});
	}
	else {
		//不存在检查是否在许可不附加storeId的列表中
		for(var nameIndex in config.exceptStoreIdInterfaces) {
			if(config.exceptStoreIdInterfaces[nameIndex] == interfaceName) {
				//存在，返回空
				deferred.resolve(false);
			}
		}
		//不存在
		deferred.reject();
	}
	return deferred.promise;
}

//连接mongodb数据库函数
var connectDB = function(storeId) {
	var deferred = Q.defer();
	//判断是否已存在该店铺的数据库连接
	for(var _storeId in dbs) {
		if(_storeId == storeId) {
			//存在，返回连接无需重连
			deferred.resolve(dbs[_storeId]);
			return deferred.promise;
		}
	}
	//连接mongodb数据库
	mongo.connect(config.mongodb.host, config.mongodb.port, storeId, config.mongodb.user, config.mongodb.pwd).then(function(db) {
		//连接成功存储连接到连接池
		dbs[storeId] = db;
		console.log(storeId + ' mongodb连接成功');
		deferred.resolve(db);
	}, function() {
		//连接失败
		console.log(storeId + ' mongodb连接失败');
		deferred.reject();
	});
	return deferred.promise;
}

//获取accessToken，一天只能获取2000次
var getAccessToken = function() {
	var deferred = Q.defer();
	//请求accessToken并存储的函数
	var getAccessTokenFun = function() {
		//发送GET请求到微信服务器取得accessToken数据
		util.get('https://api.weixin.qq.com/cgi-bin/token', {
			//小程序APPID
			appid: config.clientAppId,
			//小程序密钥APPSECRET
			secret: config.clientAppSecret,
			//指定请求类型
			grant_type: 'client_credential'
		}).then(function(jsonData) {
			//判断是否存在accessToken和accessToken有效期
			return util.isSet(jsonData.access_token, jsonData.expires_in);
		}, function() {
			//从微信服务器获取accessToken失败
			deferred.reject('-0016');
		}).then(function(jsonData) {
			//集合有效数据，jsonData来自isSet函数返回的参数列表
			var accessTokenData = {
				//accessToken值
				accessToken: jsonData[0],
				//得到当前时间戳（秒）
				timestamp: parseInt(Date.now() / 1000),
				//accessToken有效期
				expire: jsonData[1]
			};
			//存储accessToken到redis
			redis.set('accessToken', JSON.stringify(accessTokenData)).then(function() {
				//设置accessToken的生命周期（有效期，秒）
				return redis.expire('accessToken', accessTokenData.expire);
			}, function() {
				//存储accessToken到redis失败
				deferred.reject('-0023');
			}).then(function() {
				//存储accessToken成功，返回accessToken
				deferred.resolve(accessTokenData.accessToken);
			}, function() {
				//设置accessToken的生命周期失败
				deferred.reject('-0024');
			});
		}, function() {
			//从微信服务器获取的json数据中未找到accessToken
			deferred.reject('-0018');
		});
		//执行完成直接返回promise
		return deferred.promise;
	}
	//从redis获取accessToken，三重防止accessToken过期异常机制
	redis.get('accessToken').then(function(data) {
		//判断是否为null
		if(data) {
			//检查是否为json字符串
			util.isJson(data).then(function(accessTokenData) {
				console.log(accessTokenData);
				var expire = accessTokenData.timestamp + accessTokenData.expire - parseInt(Date.now() / 1000);
				if(expire > 1) {
					console.log('accessToken未过期', expire);
					//accessToken未过期，直接返回accessToken
					deferred.resolve(accessTokenData.accessToken);
				}
				else {
					console.log('accessToken已过期');
					//检查到逻辑上已过期，调用函数重新获取accessToken
					getAccessTokenFun();
				}
			}, function() {
				console.log('accessToken已过期');
				//该数据不是json字符串，调用函数重新获取accessToken
				getAccessTokenFun();
			});
		}
		else {
			console.log('accessToken已过期');
			//在redis中已过期调用函数重新获取accessToken
			getAccessTokenFun();
		}
	}, function(err) {
		console.log(err);
		//从redis读取accessToken失败
		deferred.reject('-0017');
	});
	return deferred.promise;
}
